<!DOCTYPE html>
<html>

<head>
    <meta charset="utf-8">
    <title>分片上传demo</title>
</head>
<script src="https://unpkg.com/axios/dist/axios.min.js"></script>

<body>
    <div>
        <section class="upload-section">
              <article class="upload-piclist">
                    <div class="upload-file">
                            <input type="file" id="file" accept="image/*" multiple onchange="imgChange()" />
                        </div>
                  </article>
        </section>
    </div>
</body>
<!-- <style>
  .upload-fh {
    background-repeat: no-repeat;

    background-size: 100% 100%;

    height: 30px;

    width: 30px;

  }

  .upload-hedaer {

    height: 55px;

    display: grid;

    grid-template-columns: repeat(3, 1fr);

    padding: 0 10px;

    box-sizing: border-box;

    align-items: center;

    text-align: center;

    background: #287cff;

    color: #fff;

    border-bottom: 1px solid #efefef;
    font-size: 19px;

  }

  .upload-hedaer div:last-child {

    text-align: right;

  }

  .upload-textarea {

    width: 100%;

    height: 60px;

    font-size: 28px;

    border: 1px solid #efefee;

    max-height: 300px;

  }

  .upload-article-text {

    width: 100%;

    padding: 10px;

    box-sizing: border-box;

  }

  .upload-file {

    position: relative;

    background: url('../images/z_add.png') no-repeat 50%/100% 100%;

    /* width: 100px; */

    height: 120px;

    order: 9;

  }

  .upload-piclist {

    padding: 0 10px;

    box-sizing: border-box;

    display: grid;

    grid-template-columns: repeat(3, 120px);

    justify-content: space-between;

    grid-gap: 14px;

  }

  #file {

    width: 100%;

    height: 100%;

    opacity: 0;

  }

  .upload-Picitem {

    width: 100%;

    height: 120px;

  }

  .upload-Picitem>img {

    width: 100%;

    height: 100%;

    object-fit: cover;

  }

  .submit {

    padding: 15px 0;

    background-color: #287cff;

    color: #fff;

    text-align: center;

    margin: 10px;

    font-size: 20px;

    border-radius: 10px;

  }

  .upload-sm {

    padding: 10px;

    box-sizing: border-box;

    color: gray;

  }

  .upload-sm ol>li {

    margin-bottom: 10px;

  }
</style> -->
<script>
    function S4() {
        return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
    }
    function NewGuid() {
        return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
    }
    // 初始化函数
    function imgChange(
        fileObj,//文件对象
        size = 1,//分片大小 默认2  单位m
        underWayFn = function () { },//进度条改变触发函数
        upOverFn = function () { },//上传完成触发函数
    ) {
        this.file = document.getElementById('file').files[0];
        this.size = size * 1024 * 200;
        this.underWayFn = underWayFn;
        this.upOverFn = upOverFn;
        // 判断是否是满足条件的视频对象 满足条件调用this.upbegin()处理
        this.upbegin();
    }
    // 上传文件
    async function upbegin() {
        // let videoTime = await this.videoLong();//视频播放时间

        // 视频分片
        let start = 0, end = 0, chunkArr = [], size = this.size;
        let file = this.file;
        function chuli() {
            end += size;
            var blob = file.slice(start, end);
            start += size;
            if (blob.size) {
                chunkArr.push(blob);
                chuli();
            } else {
                return chunkArr
            }
        }
        chuli();
        console.log("chunkArr", chunkArr);
        //预请求接口 然后this.inTurnto(chunkArr); 分片请求
        // 分片请求主体
        this.inTurnto(chunkArr);


        // console.log(videoTime, this.file)
    }
    // 缓慢增长到目标进度条
    function changeProgressBar(num) {
        clearInterval(this.t)
        this.t = setInterval(() => {
            if (this.progressBar == num) {
                clearInterval(this.t)
                //上传完成
                if (this.progressBar === 100) {
                    this.upOverFn();//通知上传完成
                    this.clearUpVideo();//格式化重置
                }
            } else {
                this.progressBar++;
                this.underWayFn(this.progressBar);//改变进度条通知
            }
        }, 50);
    }
    //初始请求的state值，这个值应该是在页面初始化完成后生成一个，然后之后的变更都应该放到ajax完成后去变更state值
    var state = this.NewGuid().replace(/-/g, '')
    // 多个视频一一请求
    function inTurnto(chunkArr) {
        const chunkAllNum = chunkArr.length;//片段总数量        
        let count = 0;//完成个数
        //文件令牌。一堆碎片的统一标识，用于合成文件，所以本次上传多个碎片，用一个标识
        let sharduid = this.NewGuid()

        var token = 'Tl6e-BuT2Ty_tYnN3y2D-1YjkJ0Bd54c4vOygiMU31z1gblJFBVgfU5cQdjisNpHeFz4kcRinAkzvf1Y_XwbMNLEQlh4l_7uelNSKYmmEOIkMkr7eIMsdzk1jAoPCLt2'
        let worker = function (index) {
            let shardNum = index + 1
            let item = chunkArr[index]
            // 模拟数据请求
            // setTimeout(() => {
            //   console.log(item, index)
            //   count++;//增加当前进度
            //   this.changeProgressBar(parseInt(count / chunkAllNum * 100));//改变进度
            // }, 1000);
            // let p = new Promise((resolve, reject) => {
            console.log("this.NewGuid()", this.NewGuid(), "当前切片文件", item, item.size, "总切片数", chunkAllNum, "文件名", this.file);
            var data = new FormData();
            data.append('DocType', "Product")
            data.append('ActionType', "")
            data.append('IsCompress', 'true')//是否压缩/切片/转码
            data.append('IsShard', 'true')//是否分片
            data.append('ShardUid', sharduid)//文件令牌。一堆碎片的统一标识，用于合成文件
            //这里不对
            //data.append('ContentType', item)//文件媒体类型
            //临时写个测试jpg的图片
            data.append('ContentType', "video/mp4")//文件媒体类型
            //追加文件
            data.append(this.NewGuid(), item)//文件流
            data.append('FileName', this.file.name)//文件名
            data.append('ContentLength', this.file.size)//文件大小
            data.append('ShardCount', chunkAllNum)//分片总数量
            data.append('ShardNum', shardNum)//本次上传的第几分片

            axios.post('http://localhost:5874/File/Upload?state=' + state + '&token=' + token, data).then(res => {
                console.log("res", res);
                //接口完成后变更一次state值，这才是正确的防重逻辑。
                state = this.NewGuid().replace(/-/g, '')
                if (res.status == 200) {
                    if (res.data.Code == '0') {
                        if (shardNum >= chunkAllNum) {
                            //分片都传输完成了
                            if (res.data.Data != null) {
                                //得到文件的地址令牌
                                //成功
                                alert(JSON.stringify(res.data.Data))
                            } else {
                                //报错-服务器错误
                                alert('没有获取到合成的文件')
                            }
                        } else {
                            //分片未传输完成，索引+1，递归调用
                            worker(++index)
                        }
                    } else {
                        //报错-业务错误
                        alert(res.data.Msg)
                    }
                } else {
                    //报错-HTTP状态码
                    alert(res.status)
                }
            }).catch(function (error) {
                //报错
                console.log(error);
            });
        }
        //首次开始执行，后面请求成功后再递归调用。这里为什么用递归，因为ajax是异步的，下面的for循环遍历会在一瞬间发起几百个请求，分片上传的意义没了。而且使用递归仍然可以保持ajax的异步特征，不会阻塞后续逻辑。
        worker(0)
    }
    // 获取视频总时长
    function videoLong() {
        return new Promise((resolve) => {
            var url = URL.createObjectURL(this.file);
            var audioElement = new Audio(url);
            audioElement.addEventListener("loadedmetadata", function () {
                var hour = parseInt((audioElement.duration) / 3600);
                if (hour < 10) { hour = "0" + hour; }
                var minute = parseInt((audioElement.duration % 3600) / 60);
                if (minute < 10) { minute = "0" + minute; }
                var second = Math.ceil(audioElement.duration % 60);
                if (second < 10) { second = "0" + second; }
                resolve(hour + ":" + minute + ":" + second)
            });
        })
    }
    // 重置
    function clearUpVideo() {
        this.file = null;
        this.size = 0;
        this.progressBar = 0;
        this.underWayFn = null;
        this.upOverFn = null;
        this.t = null;
    }
</script>

</html>